<?php

namespace Core;

use Core\Log;
use Core\Config;

class HttpServer
{

	const KEEP_ALIVE_TIME = 300; //keep-alive 持久连接时间

	private static function init()
	{
		Log::write("http server runing .pid :" . getmypid());
		//保存主进程的Pid
		self::_saveMasterPid();
	}

	public static function run($libserver = 'tcp://0.0.0.0:2000')
	{
		self::init();

		$socket = stream_socket_server($libserver, $errno, $errmsg, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN);
		if (!$socket) {
			Log::write('stream_socket_server() error: errno=' . $errno . ' errmsg=' . $errmsg, 'FATAL');
			exit(1);
		}
		//如果是TCP协议,打开长链接,并且禁用Nagle算法,默认为开启Nagle
		//Nagle是收集多个数据包一起发送.再实时交互场景(比如游戏)中,追求高实时性,要求一个包,哪怕再小,也要立即发送给服务端.因此我们禁用Nagle
		if (function_exists('socket_import_stream')) {
			$socket_import = socket_import_stream($socket); //not support SSL connections.
			socket_set_option($socket_import, SOL_SOCKET, SO_KEEPALIVE, 1);
			socket_set_option($socket_import, SOL_TCP, TCP_NODELAY, 1);
		}
		//使用非阻塞
		stream_set_blocking($socket, 0);

		$base = event_base_new();
		$event = event_new();
		event_set($event, $socket, EV_READ | EV_PERSIST, 'self::http_accept', $base); //EV_PERSIST 表示事件是持久的
		event_base_set($event, $base);
		event_add($event);

		#加入timer事件
		$tv_value = 1000000 * 1; //秒
		$ev_timer = event_timer_new();
		event_timer_set($ev_timer, 'self::event_timer_fun', array($ev_timer, $tv_value));
		event_base_set($ev_timer, $base);
		event_timer_add($ev_timer, $tv_value);

		event_base_loop($base);


		$GLOBALS['hq_connections'] = [];
		$GLOBALS['hq_buffers'] = [];
		$GLOBALS['hq_request'] = []; //保存所有连接的请求信息
	}

	private static function http_accept($socket, $flag, $base)
	{
		static $fid = 0;

		$connection = \stream_socket_accept($socket, 0);

		stream_set_timeout($connection, 2);
		stream_set_blocking($connection, 0);
		list($accept_ip, $accept_port) = explode(':', stream_socket_get_name($connection, true));
		Log::write("new connection \$fid:$fid accept_ip:$accept_ip \$accept_port:$accept_port");

		//注册缓冲区读
		$buffer = event_buffer_new($connection, 'self::http_read', 'self::http_write', 'self::http_error', array($base, $fid));
		if ($buffer == false)
			die('error to new buffered event.');
		event_buffer_base_set($buffer, $base);
		event_buffer_timeout_set($buffer, self::KEEP_ALIVE_TIME, self::KEEP_ALIVE_TIME);
		//event_buffer_watermark_set($buffer, EV_READ, 0, 0xffffff);// EV_READ|EV_WRITE
		event_buffer_watermark_set($buffer, EV_READ, 0, 0xffffff);
		event_buffer_watermark_set($buffer, EV_WRITE, 0, 0xffffff);
		event_buffer_priority_set($buffer, 60); //超时自动断开时间
		event_buffer_enable($buffer, EV_READ | EV_PERSIST); //EV_PERSIST:表示事件是持久的  EV_READ:FD就绪，可以读取的时候 ，事件成为激活状态
		//保存
		$GLOBALS['hq_connections'][$fid] = $connection;
		$GLOBALS['hq_buffers'][$fid] = $buffer;
		$GLOBALS['hq_request'][$fid] = []; //保存该连接的所有请求信息
		$fid++;
	}

	/**
	 * http_read
	 * @param type $buffer
	 * @param type $arr
	 */
	private static function http_read($buffer, $arr)
	{
		$fid = $arr[1];
		$request_string = '';
		while ($read = event_buffer_read($buffer, 1024))
			$request_string .= $read;

		if (isset($GLOBALS['hq_request'][$fid]['rid'])) {
			$rid = ++$GLOBALS['hq_request'][$fid]['rid'];
		} else {
			$GLOBALS['hq_request'][$fid]['rid'] = 0;
			$rid = 0;
		}
		//记录本次请求的所有信息
		$GLOBALS['hq_request'][$fid]['request'][$rid] = [];

		$request_arr = explode("\r\n", $request_string);
		Log::write("new request \$fid:{$fid} \$request_string:$request_arr[0]", 'INFO');

		//路由分析
		Route::run(new Request($request_string, $fid));
	}

	/**
	 * 测试http返回
	 * @param type $fid
	 */
	private static function response_test($fid)
	{
		$body = <<<EOF
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8"/>
	<title>test</title>
</head>
<body>
	<h1>http response test.</h1>
</body>
</html>
EOF;
		$len = strlen($body);
		$http_response = <<<EOF
HTTP/1.1 200 OK
Server: lxc-blog/1.0 (PHP)
Content-Type: text/html;charset=utf-8
Connection: keep-alive
Content-Length: $len

$body
EOF;
		event_buffer_write($GLOBALS['hq_buffers'][$fid], $http_response, strlen($http_response));
		//stream_socket_sendto($GLOBALS['hq_connections'][$fid], $http_response);
	}

	/**
	 * 测试http返回
	 * @param type $fid
	 */
	private static function sendFile_test($fid, $filename)
	{
		$len = filesize($filename);
		$cons = file_get_contents($filename);
		$content_type = mime_content_type($filename);
		$http_response = <<<EOF
HTTP/1.1 200 OK
Server: lxc-blog/1.0 (PHP)
Content-Type: $content_type
Connection: keep-alive
Content-Length: $len

$cons
EOF;
		stream_socket_sendto($GLOBALS['hq_connections'][$fid], $http_response);
	}

	/**
	 * http_write
	 * @param type $buffer
	 * @param type $error
	 * @param type $arr
	 */
	private static function http_write($buffer, $arr)
	{
		$fid = $arr[1];
		Log::write("http_write \$fid:$fid ", 'INFO');
	}

	/**
	 * http_error
	 */
	private static function http_error($buffer, $error, $arr)
	{
		$fid = $arr[1];

		//关闭用户连接
		event_buffer_disable($buffer, EV_READ | EV_WRITE);
		event_buffer_free($buffer);
		stream_socket_shutdown($GLOBALS['hq_connections'][$fid], STREAM_SHUT_WR);
		fclose($GLOBALS['hq_connections'][$fid]);
		//释放资源
		unset($buffer);


		Log::write("connection close : \$fid:$fid", 'INFO');
	}

	/**
	 * 保存主进程的Pid
	 */
	private static function _saveMasterPid()
	{
		$pid_file = Config::getInstance()->get('common.master_pid_file');
		if (false === file_put_contents($pid_file, getmypid())) {
			$string = 'Can\'t write pid to ' . $pid_file;
			Log::write($string, 'FATAL');
			exit($string);
		}
	}

	/**
	 * 定时器事件
	 */
	private static function event_timer_fun($socket, $flag, $arr)
	{
		//Log::write(date('Y-m-d H:i:s'), 'INFO');
		//event_timer_add($arr[0], $arr[1]);
	}

}
